#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <unordered_map>
#include <mutex>
#include "util.hpp"
#include "log.hpp"

//构建索引
namespace ns_index{
    //文档属性
    struct DocInfo{
        std::string _title;//文档标题
        std::string _content;//文档内容
        std::string _url;//文档url
        uint64_t  _id;//文档id
    };
    //倒排索引
    struct InvertedElem{
        std::string _word;//关键字
        uint64_t _id;//id
        int _weight;//权重
        InvertedElem():_weight(0){}
    };
    //倒排拉链
    typedef std::vector<InvertedElem> InvertedList;
    //索引
    class index
    {
    private:
        index(){};
        index(const index&)=delete;
        index operator=(const index&)=delete;
    public:
        static index* GetIndex()//初始化单例模式，在这里我们使用懒汉模式来建立索引
        {
            if(nullptr==_instance)
            {
                _mtx.lock();//多线程问题
                if(nullptr==_instance)
                {
                    _instance=new index();

                }
                _mtx.unlock();
            }
            return _instance;
        }
    public:
        //正排索引
        DocInfo *GetForwardIndex(uint64_t _id)//根据文档id找文档内容
        {
            if(_id>=_forward_index.size())//数组下标从0开始
            {
                std::cerr << "doc_id out range, error!" << std::endl;
                return nullptr;
            }
            return &_forward_index[_id];
        }
        //根据关键字，获得文档倒排拉链，倒排索引
        InvertedList *GetInvertedList(const std::string &word)
        {
            auto iter = _inverted_index.find(word);//寻找关键字
            if(iter==_inverted_index.end())//没找到
            {
                std::cerr << "_inverted_index out range, error!" << std::endl;
                return nullptr;
            }
            return &(iter->second);
        }
        //根据去标签，格式化之后的文档，构建正排和倒排索引
        bool BuildIndex(const std::string &input)
        {
            std::ifstream in(input,std::ios::in|std::ios::binary);
            if(!in.is_open())
            {
                std::cerr<<" open file error1"<<std::endl;
            }
            std::string line;
            int count=0;
            while (std::getline(in, line))//读取内容
            {
                DocInfo *doc = BuildForwardIndex(line);
                if (nullptr == doc)
                {
                    std::cerr << "build " << line << " error" << std::endl; // for deubg
                    continue;
                }
                BuildInvertedIndex(*doc);
                count++;
            }
            return true;
        }

    private:
    DocInfo* BuildForwardIndex(const std::string &line)
    {
        //进行字符串切分
        std::vector<std::string> results;
        const std::string sep = "\3";   //行内分隔符
        ns_util::StringUtil::Split(line, &results, sep);//Boost分词
        if(results.size()!=3)
        {
            std::cerr<<"split error"<<std::endl;
            return nullptr;
        }
        //2. 字符串进行填充到DocIinfo
        DocInfo doc;
        doc._title = results[0];//title
        doc._content = results[1];// content
        doc._url = results[2];// url
        doc._id = _forward_index.size(); // 先进行保存id，在插入，对应的id就是当前doc在vector中的下标!
        // 3. 插入到正排索引的vector
        _forward_index.push_back(std::move(doc)); // doc,html文件内容
        return &_forward_index.back();
    }
    bool BuildInvertedIndex(const DocInfo &doc)
    {
        // DocInfo{title, content, url, doc_id}
        // word -> 倒排拉链
        struct word_cnt
        {
            int _title_cnt;
            int _content_cnt;
            word_cnt() : _title_cnt(0), _content_cnt(0) {}
        };

        std::unordered_map<std::string, word_cnt> word_map; // 用来暂存词频的映射表

        // 对标题进行分词
        std::vector<std::string> title_words;
        ns_util::JiebaUtil::CutString(doc._title, &title_words);//分词
        //计算权重
        for(auto e:title_words)
        {
            boost::to_lower(e);      // 需要统一转化成为小写
            word_map[e]._title_cnt++; // 如果存在就获取，如果不存在就新建
        }
        //内容分词
        std::vector<std::string> content_words;
        ns_util::JiebaUtil::CutString(doc._content, &content_words);//分词
        for (std::string e : content_words)
        {
            boost::to_lower(e);
            word_map[e]._content_cnt++;
        }
        //权重计算
        #define X 10
        #define Y 1
        for(auto e:word_map)
        {
            InvertedElem item;
            item._id = doc._id;
            item._word = e.first;
            item._weight = X * e.second._title_cnt + Y * e.second._content_cnt; // 相关性
            InvertedList &inverted_list = _inverted_index[e.first];
            inverted_list.push_back(std::move(item));
        }
        return true;
    }
    private:
    std::vector<DocInfo> _forward_index;//正排
    //倒排索引一定是一个关键字和一组(个)InvertedElem对应[关键字和倒排拉链的映射关系]
    std::unordered_map<std::string, InvertedList> _inverted_index;

    static index *_instance;
    static std::mutex _mtx;
    };

    index* index::_instance = nullptr;
    std::mutex index::_mtx;
}